package controller;

import eccezioni.AmministratoreNonBannabileException;
import eccezioni.UtenteGiaBannatoException;
import eccezioni.UtenteGiaModeratoreException;
import eccezioni.UtenteNonAmministratoreException;
import eccezioni.UtenteNonModeratoreException;
import entity.Adesione;
import entity.Annuncio;
import entity.Articolo;
import entity.Feedback;
import entity.IndirizzoEmail;
import entity.ModalitaPagamento;
import entity.UtenteRegistrato;
import java.util.LinkedList;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import sbForEntityClasses.AdesioneFacadeLocal;
import sbForEntityClasses.AnnuncioFacadeLocal;
import sbForEntityClasses.ArticoloFacadeLocal;
import sbForEntityClasses.FeedbackFacadeLocal;
import sbForEntityClasses.IndirizzoEmailFacadeLocal;
import sbForEntityClasses.UtenteRegistratoFacadeLocal;

/**
 * Metodi per i moderatori e l'amministratore
 * @author Ale
 */
@Stateless
public class ControllerAmministrazione implements ControllerAmministrazioneLocal {
    @EJB
    private ControllerUtentiLocal controllerUtenti;
    @EJB
    private ArticoloFacadeLocal articoloFacade;
    @EJB
    private AdesioneFacadeLocal adesioneFacade;
    @EJB
    private AnnuncioFacadeLocal annuncioFacade;
    @EJB
    private ControllerMail controllerMail;
    @EJB
    private IndirizzoEmailFacadeLocal indirizzoEmailFacade;
    @EJB
    private FeedbackFacadeLocal feedbackFacade;
    @EJB
    private UtenteRegistratoFacadeLocal utenteRegistratoFacade;
    @PersistenceContext(unitName = "ShippingShareBisBis-ejbPU")
    private EntityManager em;

    private boolean isThereAmministratore() {
        String query = "SELECT x FROM UtenteRegistrato x WHERE x.ruolo = ?1";
        Query q = em.createQuery(query);
        q.setParameter(1, UtenteRegistrato.Ruolo.AMMINISTRATORE);
        try {
            UtenteRegistrato ur = (UtenteRegistrato) q.getSingleResult();
            return true;
        } catch (NoResultException nre) {
            return false;
        }
    }

    /**
     * Crea l'amministratore del sistema, se non è già presente
     */
    @Override
    public void creaAmministratore() {
        if (isThereAmministratore()) {
            return;
        }
        UtenteRegistrato amm = new UtenteRegistrato();

        amm.setAdesioni(new LinkedList<Adesione>());
        amm.setAnnunciCreati(new LinkedList<Annuncio>());
        amm.setBannato(false);

        Feedback feed = new Feedback();
        feed.setFeedbackCreatore(5);
        feed.setFeedbackIscritto(5);
        feed.setNumFeedbackCreatore(1);
        feed.setNumFeedbackIscritto(1);
        feedbackFacade.create(feed);
        amm.setFeedback(feed);


        IndirizzoEmail email = new IndirizzoEmail();
        email.setIndirizzo("mail.shippingshare@gmail.com");
        indirizzoEmailFacade.create(email);
        amm.setEmail(email);


        amm.setIndirizzo(controllerUtenti.creaIndirizzo("via Pessinetto 12, Torino"));

        amm.setModalitaPagamento(new LinkedList<ModalitaPagamento>());
        amm.setNome("Amministratore di Shipping Share");
        amm.setCognome("");
        amm.setTelefono("011777777");
        amm.setCrittoPassword("sscsweb2011");
        amm.setRuolo(UtenteRegistrato.Ruolo.AMMINISTRATORE);

        utenteRegistratoFacade.create(amm);
    }

    /**
     * Banna un utente
     * @param bannando utente da bannare
     * @param bannatore moderatore/amministratore che esegue l'operazione
     * @throws UtenteNonModeratoreException se l'utente che esegue l'operazione non è almeno moderatore
     * @throws UtenteGiaBannatoException se l'utente da bannare è già stato bannato
     * @throws AmministratoreNonBannabileException se l'utente da bannare è amministratore e quindi non bannabile
     */
    @Override
    public void bannaUtente(UtenteRegistrato bannando, UtenteRegistrato bannatore) throws UtenteNonModeratoreException, UtenteGiaBannatoException, AmministratoreNonBannabileException, UtenteNonAmministratoreException {
        if (!bannatore.isModeratore()) {
            throw new UtenteNonModeratoreException("Solo un moderatore pu&ograve; bannare un utente.");
        } else if (bannando.isBannato()) { 
            throw new UtenteGiaBannatoException("L'utente &egrave; gi&agrave; bannato.");
        } else if (bannando.isAmministratore()) {
            throw new AmministratoreNonBannabileException("Non &egrave; possibile bannare l'amministratore.");
        } else if (bannando.isModeratore() && !bannatore.isAmministratore()){
            throw new UtenteNonAmministratoreException("Soltanto l'amministratore pu&ograve; bannare un moderatore");
        }

        bannando.setBannato(true);

        int nAnnunci = bannando.getAnnunciCreati().size();
        for (int i = 0; i < nAnnunci; nAnnunci--) {
            Annuncio a = bannando.getAnnunciCreati().get(i);
            for (Adesione ad : a.getAdesioni()) {
                controllerMail.sendMailBannatoAnnuncioCancellato(bannando, ad.getIscritto(), a.getSito());
            }
            cancellaAnnuncio(a);
        }

        int nAdesioni = bannando.getAdesioni().size();
        for (int i = 0; i < nAdesioni; nAdesioni--) {
            Adesione a = bannando.getAdesioni().get(i);
            controllerMail.sendMailBannatoAdesioneCancellata(a.getAnnuncio().getCreatoreAnnuncio(), bannando, a.getAnnuncio().getSito());
            cancellaAdesione(a);
        }
        utenteRegistratoFacade.edit(bannando);
    }

    /**
     * Assegna d un utente semplice i privilegi di moderatore
     * @param utente utente a cui fornire i privilegi
     * @param amministratore l'amministratore dei sistema che dà i privilegi
     * @throws UtenteNonAmministratoreException se chi esegue l'operazione non è amministratore
     * @throws UtenteGiaModeratoreException se l'utente da elevare a moderatore è già moderatore
     */
    @Override
    public void promuoviAModeratore(UtenteRegistrato utente, UtenteRegistrato amministratore) throws UtenteNonAmministratoreException, UtenteGiaModeratoreException {
        if (!amministratore.isAmministratore()) {
            throw new UtenteNonAmministratoreException("Solo l'amministratore pu&ograve; promuovere un utente a moderatore.");
        } else if (utente.isModeratore()) {
            throw new UtenteGiaModeratoreException("L'utente &egrave; gi&agrave; un moderatore.");
        }

        utente.setRuolo(UtenteRegistrato.Ruolo.MODERATORE);
        utenteRegistratoFacade.edit(utente);
    }

    /**
     * Declassa un utente da moderatore a utente semplice.
     * @param utente moderatore da declassare
     * @param amministratore amministratore che declassa l'utente 
     * @throws UtenteNonAmministratoreException se chi esegue l'operazione non è amministratore
     * @throws UtenteNonModeratoreException se l'utente da declassare non è moderatore
     */
    @Override
    public void declassaDaModeratore(UtenteRegistrato utente, UtenteRegistrato amministratore) throws UtenteNonAmministratoreException, UtenteNonModeratoreException {
        if (!amministratore.isAmministratore()) {
            throw new UtenteNonAmministratoreException("Solo l'amministratore pu&ograve; declassare un moderatore a utente.");
        } else if (!utente.isModeratore()) {
            throw new UtenteNonModeratoreException("L'utente non &egrave; un moderatore.");
        }

        utente.setRuolo(UtenteRegistrato.Ruolo.UTENTE);
        utenteRegistratoFacade.edit(utente);
    }


    /**
     * Cancella un annuncio dal sistema
     * @param a annuncio da cancellare
     */
    @Override
    public void cancellaAnnuncio(Annuncio a) {

        UtenteRegistrato creatore = a.getCreatoreAnnuncio();
        creatore.getAnnunciCreati().remove(a);
        utenteRegistratoFacade.edit(creatore);

        int nAdesioni = a.getAdesioni().size();
        for (int i = 0; i < nAdesioni; i++) {
            Adesione ad = a.getAdesioni().get(0);
            cancellaAdesione(ad);
        }

        a.getAdesioni().clear();
        annuncioFacade.remove(a);
    }

    /**
     * Cancella un'adesione dal sistema
     * @param ad adesione da cancellare
     */
    @Override
    public void cancellaAdesione(Adesione ad) {
        UtenteRegistrato iscritto = ad.getIscritto();
        Annuncio annuncio = ad.getAnnuncio();

        iscritto.getAdesioni().remove(ad);
        utenteRegistratoFacade.edit(iscritto);
        annuncio.getAdesioni().remove(ad);

        for (Articolo art : ad.getArticoli()) {
            articoloFacade.remove(art);
        }

        ad.getArticoli().clear();
        adesioneFacade.remove(ad);
        annuncioFacade.edit(annuncio);
    }

    /**
     * Chiude un annuncio perché scaduto. 
     * Il metodo alla scadenza dell'annuncio marca tutte le adesioni e gli articoli 
     * non esplicitamente accettati come rifiutati, e notifica creatore e iscritti
     * @param a
     */
    @Override
    public void chiudiAnnuncioScaduto(Annuncio a) {

        controllerMail.sendMailAnnuncioScadutoCreatore(a.getCreatoreAnnuncio(), a);

        //tutti gli articoli e le adesioni in sospeso sono automaticamente rifiutati
        for (Adesione ad : a.getAdesioni()) {
            if (ad.getStato().equals(Adesione.StatoAdesione.IN_SOSPESO)) {
                ad.setStato(Adesione.StatoAdesione.RIFIUTATA);
                adesioneFacade.edit(ad);
                controllerMail.sendMailAnnuncioScadutoIscritto(a.getCreatoreAnnuncio(), ad.getIscritto(), a);
            }

            for (Articolo art : ad.getArticoli()) {
                if (art.getStato().equals(Articolo.Stato.IN_SOSPESO)) {
                    art.setStato(Articolo.Stato.RIFIUTATO);
                    articoloFacade.edit(art);
                }
            }
        }
    }

    /**
     * Persistenza
     * @param object
     */
    public void persist(Object object) {
        em.persist(object);
    }
    
}
